﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Net;
using BGSpaceShooter;
using Microsoft.Xna.Framework;

namespace BGSpaceShooterServer
{
    internal class NetworkController
    {
        private int port;
        private List<UDPListener> udpListeners;

        public NetworkController(int port)
        {
            this.port = port;
            StartListening();
        }

        private void StartListening()
        {
            udpListeners = new List<UDPListener>();

            IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());

            int i = 0;
            foreach (IPAddress ip in ips)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    udpListeners.Add(new UDPListener(ip, port + i++));
                }
            }
            udpListeners.Add(new UDPListener(IPAddress.Loopback, port + i++));

            foreach (UDPListener udpListener in udpListeners)
            {
                udpListener.MessageRecieved += new CTSEventHandler(onMessageRecieved);
            }
        }

        private void onMessageRecieved(object sender, CTSEventArgs eventArgs)
        {
            if (MessageRecieved != null)
            {
                MessageRecieved.Invoke(sender, eventArgs);
            }
        }

        public void ChangeServerState(ServerState serverState, IEnumerable<Player> players)
        {
            if (serverState == ServerState.Exiting && players != null)
            {
                SendQuitMessage(players);
            }
            foreach (UDPListener udpListener in udpListeners)
            {
                udpListener.ChangeState(serverState);
            }
        }

        private void SendQuitMessage(IEnumerable<Player> players)
        {
            ServerToClientPacket serverToClientPacket = new ServerToClientPacket();

            serverToClientPacket.MessageType = MessageType.EndGame;

            foreach (Player player in players)
            {
                serverToClientPacket.PlayerID = player.PlayerID;

                SendMessage(new IPEndPoint(player.PlayerAddress, player.PlayerPort), serverToClientPacket);
            }
        }

        public static string[] GetIPs()
        {
            IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());

            List<string> result = new List<string>();

            foreach (IPAddress ipAddress in ips)
            {
                if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
                {
                    result.Add(ipAddress.ToString());
                }
            }

            return result.ToArray();
        }

        /// <summary>
        /// Pokazuje, czy wszystkie połączenia zostały zamknięte.
        /// </summary>
        public bool ConnectionClosed
        {
            get
            {
                bool closed = true;
                foreach (UDPListener udpListener in udpListeners)
                {
                    closed &= udpListener.Closed;
                }
                return closed;
            }
        }

        private void SendMessage(IPEndPoint address, ServerToClientPacket serverToClientPacket)
        {
            new UDPMessenger(address.Address, address.Port).SendMessage(serverToClientPacket);
        }

        public void SendAcceptJoinMessage(IPEndPoint address, int playerID)
        {
            ServerToClientPacket serverToClientPacket = new ServerToClientPacket();
            serverToClientPacket.MessageType = MessageType.AcceptJoin;
            serverToClientPacket.PlayerID = playerID;

            SendMessage(address, serverToClientPacket);
        }

        public void SendStartGameInitialization(IPEndPoint address, IEnumerable<Player> players)
        {
            ServerToClientPacket serverToClientPacket = new ServerToClientPacket();

            serverToClientPacket.MessageType = MessageType.StartGame;

            SendMessage(address, serverToClientPacket);

            serverToClientPacket.MessageType = MessageType.Event;
            serverToClientPacket.EventType = EventType.Creation;

            foreach (Player player in players)
            {
                serverToClientPacket.PlayerID = player.PlayerID;

                ServerStatusResponse serverStatusResponse = player.GetStatusResponse();

                serverToClientPacket.ServerStatusResponse = serverStatusResponse;

                SendMessage(address, serverToClientPacket);

            }

            serverToClientPacket.MessageType = MessageType.EndStartGame;
            SendMessage(address, serverToClientPacket);

        }

        public void SendDenyJoinMessage(IPEndPoint address)
        {
            ServerToClientPacket serverToClientPacket = new ServerToClientPacket();
            serverToClientPacket.MessageType = MessageType.DenyJoin;

            SendMessage(address, serverToClientPacket);
        }

        public void SendUpdates(IEnumerable<Player> players, IEnumerable<ServerStatusResponse> responses)
        {
            DateTime now = DateTime.Now;
            foreach (ServerStatusResponse serverStatusResponse in responses)
            {
                serverStatusResponse.SetTimeStamp(now);
                ServerToClientPacket serverToClientPacket = new ServerToClientPacket();
                serverToClientPacket.MessageType = MessageType.Update;
                serverToClientPacket.PlayerID = serverStatusResponse.playerId;
                serverToClientPacket.ServerStatusResponse = serverStatusResponse;

                foreach (Player player in players)
                {
                    SendMessage(new IPEndPoint(player.PlayerAddress, player.PlayerPort), serverToClientPacket);
                }
            }
        }

        public void SendEvents(IEnumerable<Player> players, IEnumerable<ServerStatusResponse> responses, EventType eventType)
        {
            foreach (ServerStatusResponse serverStatusResponse in responses)
            {
                ServerToClientPacket serverToClientPacket = new ServerToClientPacket();
                serverToClientPacket.MessageType = MessageType.Event;
                serverToClientPacket.PlayerID = -1;
                serverToClientPacket.ServerStatusResponse = serverStatusResponse;
                serverToClientPacket.EventType = eventType;

                foreach (Player player in players)
                {
                    SendMessage(new IPEndPoint(player.PlayerAddress, player.PlayerPort), serverToClientPacket);
                }
            }
        }

        public event CTSEventHandler MessageRecieved;
    }
}
